// Base Scene Node
// (c) jimon game studio

#ifndef JEH_ISCENENODE
#define JEH_ISCENENODE

#include "jeUnknown.h"
#include "jeStl.h"
#include "ISerialize.h"
#include "IRender.h"
#include "IAnimator.h"
#include "jePassGroups.h"

namespace je
{
	namespace scene
	{
		//! Base Scene Node
		class ISceneNode:public je::core::ISerialize
		{
		public:
			//! Childrens List Type
			typedef jeList<ISceneNode*> ChildrensListType;

			//! Animators List Type
			typedef jeList<IAnimator*> AnimatorsListType;
		protected:
			//! Current Render
			video::IRender * CurrentRender;

			//! Scene Node Name
			jeStringd NodeName;

			//! SceneNode Model Matrix
			jeMatrix4f ModelMatrix;

			//! SceneNode Absolute Model Matrix
			jeMatrix4f AbsoluteModelMatrix;

			//! SceneNode Absolute Scale
			jeVector3df AbsoluteScale;

			//! Position
			jeVector3df CurrentPosition;

			//! Rotation
			jeVector3df CurrentRotation;

			//! Scale
			jeVector3df CurrentScale;

			//! Current Layer
			s32 CurrentLayer;

			//! Visible
			u1 Visible;

			//! SceneNode Texture Matrix
			jeMatrix4f TextureMatrix;

			//! Parent
			ISceneNode * Parent;

			//! Childrens
			ChildrensListType ChildrensList;

			//! Animators
			AnimatorsListType AnimatorsList;

			//! Need To Rebuild Matrix
			u1 NeedToRebuildMatrix;

			//! In frame
			u1 InFrame;
		public:
			//! Constructor
			ISceneNode(video::IRender * Render,const jeStringd & SetNodeName)
				:CurrentRender(Render),NodeName(SetNodeName),Parent(NULL),NeedToRebuildMatrix(true),CurrentLayer(0),
				Visible(true),InFrame(true)
			{
				AbsoluteScale.SetXYZ(1,1,1);
				CurrentScale.SetXYZ(1,1,1);
			}

			//! Destructor
			virtual ~ISceneNode()
			{
				for(AnimatorsListType::Iterator Iter = AnimatorsList.begin();Iter != AnimatorsList.end();Iter++)
					(*Iter)->Drop();
				AnimatorsList.clear();

				for(ChildrensListType::Iterator Iter = ChildrensList.begin();Iter != ChildrensList.end();Iter++)
					(*Iter)->Drop();
				ChildrensList.clear();
			}

			//! On Update
			virtual void OnUpdate()
			{
				for(AnimatorsListType::Iterator Iter = AnimatorsList.begin();Iter != AnimatorsList.end();Iter++)
					(*Iter)->OnUpdate();
			}

			//! On Render
			virtual void OnRender()
			{
			}
			
			//! On Visibility Culling
			virtual void OnVisibilityCulling()
			{
			}

			//! Set Name
			virtual void SetName(const jeStringd & NewName)
			{
				NodeName = NewName;
			}

			//! Get Name
			virtual const jeStringd & GetName()
			{
				return NodeName;
			}

			//! Get Node By Name
			ISceneNode * GetNodeByName(const jeStringd & NodeName)
			{
				if(GetName() == NodeName)
					return this;

				for(ChildrensListType::Iterator Iter = ChildrensList.begin();Iter!=ChildrensList.end();Iter++)
				{
					ISceneNode * ReturnNode = (*Iter)->GetNodeByName(NodeName);
					if(ReturnNode)
						return ReturnNode;
				}

				return NULL;
			}

			//! Set Position
			virtual void SetPosition(const jeVector3df & Position,u1 Relative = false)
			{
				if(Relative)
					CurrentPosition += Position;
				else
					CurrentPosition = Position;
				NeedToRebuildMatrix = true;
			}

			//! Get Position
			virtual const jeVector3df & GetPosition()
			{
				return CurrentPosition;
			}

			//! Set Rotation
			virtual void SetRotation(const jeVector3df & Rotation,u1 Relative = false)
			{
				if(Relative)
					CurrentRotation += Rotation;
				else
					CurrentRotation = Rotation;
				NeedToRebuildMatrix = true;
			}

			//! Get Rotation
			virtual const jeVector3df & GetRotation()
			{
				return CurrentRotation;
			}

			//! Set Scale
			virtual void SetScale(const jeVector3df & Scale,u1 Relative = false)
			{
				if(Relative)
					CurrentScale = CurrentScale * Scale;
				else
					CurrentScale = Scale;
				NeedToRebuildMatrix = true;
			}

			//! Get Scale
			virtual const jeVector3df & GetScale()
			{
				return CurrentScale;
			}

			//! Rebuild Transformations
			virtual void RebuildTransformations()
			{
				if(NeedToRebuildMatrix)
				{
					ModelMatrix.Identity();
					ModelMatrix.Scale(CurrentScale);
					ModelMatrix.Rotate(CurrentRotation);
					ModelMatrix.Translate(CurrentPosition);
					NeedToRebuildMatrix = false;
				}
			}

			//! Set Model Matrix
			virtual void SetModelMatrix(const jeMatrix4f & NewModelMatrix)
			{
				ModelMatrix = NewModelMatrix;
			}

			//! Get Model Matrix
			virtual const jeMatrix4f & GetModelMatrix()
			{
				return ModelMatrix;
			}

			//! Get Absolute Model Matrix
			virtual const jeMatrix4f & GetAbsoluteModelMatrix()
			{
				return AbsoluteModelMatrix;
			}

			//! Set Texture Matrix
			virtual void SetTextureMatrix(const jeMatrix4f & NewTextureMatrix)
			{
				TextureMatrix = NewTextureMatrix;
			}

			//! Get Texture Matrix
			virtual const jeMatrix4f & GetTextureMatrix()
			{
				return TextureMatrix;
			}

			//! Build Absolute Model Matrix
			virtual void BuildAbsoluteModelMatrix(const jeMatrix4f & ParentMatrix,const jeVector3df & ParentScale,u1 Changed)
			{
				if(NeedToRebuildMatrix)
					Changed = true;

				RebuildTransformations();

				if(Changed)
				{
					AbsoluteModelMatrix = ParentMatrix * ModelMatrix;
					AbsoluteScale = ParentScale * CurrentScale;
				}

				for(ChildrensListType::Iterator Iter = ChildrensList.begin();Iter != ChildrensList.end();Iter++)
					(*Iter)->BuildAbsoluteModelMatrix(AbsoluteModelMatrix,AbsoluteScale,Changed);
			}

			//! Set Parent
			virtual void SetParent(ISceneNode * NewParent)
			{
				Parent = NewParent;
			}

			//! Get Parent
			virtual ISceneNode * GetParent()
			{
				return Parent;
			}

			//! Add Children Node
			virtual void AddChildren(ISceneNode * Node)
			{
				Node->Grab();

				if(Node->GetParent())
					Node->GetParent()->RemoveChildren(Node);

				ChildrensList.push_back(Node);
				Node->SetParent(this);
			}

			//! Remove Children
			virtual void RemoveChildren(ISceneNode * Node)
			{
				for(ChildrensListType::Iterator Iter = ChildrensList.begin();Iter != ChildrensList.end();Iter++)
					if ((*Iter) == Node)
					{
						ChildrensList.erase(Iter);
						Node->SetParent(NULL);
						Node->Drop();
						return;
					}
			}

			//! Return Childrens List
			virtual ChildrensListType & GetChildrensList()
			{
				return ChildrensList;
			}

			//! Remove Itself
			virtual void RemoveItself()
			{
				if(Parent)
					Parent->RemoveChildren(this);
				else
					this->Drop();
			}

			//! Set Layer
			virtual void SetLayer(s32 Layer)
			{
				CurrentLayer = Layer;
			}

			//! Get Layer
			virtual s32 GetLayer()
			{
				return CurrentLayer;
			}
		
			//! Set Visible
			virtual void SetVisible(u1 visible)
			{
				Visible = visible;
				for(ChildrensListType::Iterator Iter = ChildrensList.begin();Iter != ChildrensList.end();Iter++)
					(*Iter)->SetVisible(visible);
			}

			//! Return true if object visible
			inline u1 IsVisible()
			{
				return Visible;
			}

			//! Set inframe
			virtual void SetInFrame(u1 inframe)
			{
				InFrame = inframe;
			}

			//! Return true if object in current frame (frustum culling,etc)
			inline u1 IsInFrame()
			{
				return InFrame;
			}

			//! Add Animator
			virtual void AddAnimator(IAnimator * Animator)
			{
				AnimatorsList.push_back(Animator);
				Animator->SetNode(this);
				Animator->Grab();
			}

			//! Remove Animator
			virtual void RemoveAnimator(IAnimator * Animator)
			{
				for(AnimatorsListType::Iterator Iter = AnimatorsList.begin();Iter != AnimatorsList.end();Iter++)
					if((*Iter) == Animator)
					{
						AnimatorsList.erase(Iter);
						Animator->SetNode(NULL);
						Animator->Drop();
						return;
					}
			}

			//! Return Animators List
			virtual AnimatorsListType & GetAnimatorsList()
			{
				return AnimatorsList;
			}

			//! Serialize
			virtual void Serialize(core::ISerializeStream * SerializeStream)
			{
				JE_SS_BEGIN(SceneNode)
				JE_SS(NodeName)
				JE_SS_LINEAR_BLOCK_BEGIN(NodeData)
				JE_SS(CurrentPosition)
				JE_SS(CurrentRotation)
				JE_SS(CurrentScale)
				JE_SS(CurrentLayer)
				JE_SS(Visible)
				JE_SS(TextureMatrix)
				JE_SS_LINEAR_BLOCK_END(NodeData)
				JE_SS_END(SceneNode)
			}

			//! Get Node Type Name
			virtual jeStringc GetNodeTypeName() = 0;
		};
	}
}

#endif
